/*
 * Copyright 2010 J Patrick Davenport
 * 
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 * 
 * http://www.apache.org/licenses/LICENSE-2.0
 * 
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
package v4view.core;

import java.io.IOException;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;

import org.apache.commons.collections.CollectionUtils;
import org.apache.commons.lang.StringUtils;

/**
 * The base class of the whole library. This represents the simplest concept of a page element, be it JSON, XML or HTML.
 * @author J Patrick Davenport
 *
 */
public abstract class Element {
	private List<Attribute>	attributes;
	private boolean			supportsAttributes;

	/**
	 * Default constructor place holder.
	 */
	public Element() {
	}

	public boolean supportsAttributes() {
		return this.supportsAttributes;
	}

	/**
	 * Specifies if the component supports the use of attributes;
	 * @param _supportsAttributes
	 */
	protected void setSupportsAttributes(final boolean _supportsAttributes) {
		this.supportsAttributes = _supportsAttributes;
	}

	/**
	 * Tells if the element has any attributes.
	 * @return true if this element has attributes; false otherwise.
	 * @throws AttributesNotSupportedException if the element does not support attributes.
	 */
	public boolean hasAttributes() throws AttributesNotSupportedException {
		this.guardAttributeSupport();
		return CollectionUtils.isNotEmpty(this.attributes);
	}

	public String renderAttribute() throws AttributesNotSupportedException {
		if (this.supportsAttributes()) {
			this.guardAttributes();
			final StringBuilder sb = new StringBuilder();
			for (final Attribute a : this.attributes) {
				sb.append(a.toString());
				sb.append(" ");
			}

			return sb.toString();
		}
		return "";
	}

	/**
	 * Adds new attribute value to the Element. If the attribute already exists, it is overwritten.
	 * @param _name The name of attribute.
	 * @param _value The value for the attribute. Allows empty string.
	 * @return the Element for chaining.
	 * @throws IllegalArgumentException if the _name is empty.
	 */
	public Element setAttribute(final String _name, final String _value) throws IllegalArgumentException, AttributesNotSupportedException {
		if (StringUtils.isEmpty(_name)) throw new IllegalArgumentException("_name cannot be empty");
		this.guardAttributes();
		final Attribute a = new Attribute(_name, _value);
		if (this.attributes.contains(a)) {
			this.attributes.remove(a);
		}
		this.attributes.add(a);

		return this;
	}

	/**
	 * Gets the attribute for the given index. 
	 * @param _name attribute being sought.
	 * @return The attribute associated with the name, or null if no association.
	 * @throws AttributesNotSupportedException
	 */
	public Attribute getAttribute(final String _name) throws AttributesNotSupportedException {
		this.guardAttributes();
		for (final Attribute a : this.attributes) {
			if (a.getName().equals(_name)) return a;
		}

		return null;
	}

	/**
	 * Helper method for getAttribute.
	 * @param _name
	 * @return A value if an attribute was set, or a null.
	 */
	public String getAttributeValue(final String _name) {
		final Attribute a = this.getAttribute(_name);
		if (a == null) {
			return null;
		} else {
			return a.getValue();
		}
	}

	public void removeAttribute(final String _string) {
		final Iterator<Attribute> attributes2 = this.getAttributes();
		for (Attribute a = null; attributes2.hasNext();) {
			a = attributes2.next();
			if (a != null && a.getName().equals(_string)) {
				attributes2.remove();
				break;
			}
		}
	}

	public Iterator<Attribute> getAttributes() throws AttributesNotSupportedException {
		this.guardAttributes();
		return this.attributes.iterator();
	}

	/**
	 * Second phase in the page rendering life-cycle. This is where application specific logic of the component should go.
	 */
	public abstract void load();

	public abstract void render() throws IOException;

	private void guardAttributes() {
		if (this.attributes == null) {
			this.attributes = new ArrayList<Attribute>();
		}
		this.guardAttributeSupport();
	}

	private void guardAttributeSupport() throws AttributesNotSupportedException {
		if (!this.supportsAttributes) { throw new AttributesNotSupportedException(this.getClass().getSimpleName() + " does not support attributes"); }
	}
}
